#pragma once

#include <iostream>
#include <memory>
#include <string>
#include <functional>
#include <unordered_map>

#include "Sock.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include "Comm.hpp"

uint32_t EVENT_IN = (EPOLLIN | EPOLLET); // ET模式
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
static const int g_buffer_size = 128;

class Connection;
class TcpServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock, std::shared_ptr<TcpServer> tcp_server_ptr)
        : _sock(sock), _tcp_server_ptr(tcp_server_ptr)
    {
    }

    // 设置回调方法
    void SetHandle(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }

    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }

    std::string &InBuffer()
    {
        return _inbuffer;
    }

    std::string &OutBuffer()
    {
        return _outbuffer;
    }
    int SockFd()
    {
        return _sock;
    }

    ~Connection()
    {
    }

private:
    int _sock;
    std::string _inbuffer;  // 每个套接字的接收缓冲区
    std::string _outbuffer; // 每个套接字的发送缓冲区

public:
    func_t _recv_cb; // 读取回调函数
    func_t _send_cb; // 接受回调函数
    func_t _except_cb;

    std::shared_ptr<TcpServer> _tcp_server_ptr; // 添加一个回指指针

    std::string _ip;
    uint16_t _port;
};

// et模式的 epoll，只会向上层提醒一次，所以必须循环读取接收缓冲区的内容，所以文件描述符必须是非阻塞,fcntl
class TcpServer
{
    static const int num = 64;

public:
    TcpServer(uint16_t port, func_t OnMessage)
        : _port(port), _listensock_ptr(new Sock()), _epoller_ptr(new Epoller), _OnMessage(OnMessage)
    {
    }

    void Init()
    {
        _listensock_ptr->Socket();
        // 设置文件描述符为非阻塞状态
        SetNonBlockOrDie(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        std::cout << "create listen socket success, fd:" << _listensock_ptr->Fd() << std::endl;

        // 初始化时就要创建fd对应的connection,传入对应的参数，listensock只需要关心读事件
        AddConnection(_listensock_ptr->Fd(), EVENT_IN,
                      std::bind(&TcpServer::Accept, this, std::placeholders::_1), nullptr, nullptr);
    }

    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb,
                       const std::string &ip = "0.0.0.0", uint16_t port = 0)
    {
        // 1.要给sock建立一个connection对象，把listensock放入到connection，
        std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sock, std::shared_ptr<TcpServer>(this));
        // 设置回调函数
        new_connection->SetHandle(recv_cb, send_cb, except_cb);

        // 设置ip和port
        new_connection->_ip = ip;
        new_connection->_port = port;

        // 2.再把listensock和对应的connection放入到unorderedmap中
        _connections.insert(std::make_pair(sock, new_connection));
        // 3.添加对应的事件，加入到内核之中,即添加到epoll模型的红黑树中
        _epoller_ptr->EpollUpdate(EPOLL_CTL_ADD, sock, event);
    }

    void Accept(std::shared_ptr<Connection> connection)
    {
        while (1)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->SockFd(), (struct sockaddr *)&peer, &len);
            if (sock > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                std::cout << "get a new link,peerip:" << ipbuf << ",peerport:" << peerport << ",sockfd:" << sock << std::endl;

                // 设置非阻塞
                SetNonBlockOrDie(sock);
                // 建立对应的connection，传入普通套接字的回调函数
                AddConnection(sock, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, placeholders::_1),
                              std::bind(&TcpServer::Sender, this, placeholders::_1),
                              std::bind(&TcpServer::Sender, this, placeholders::_1));
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                    break;
            }
        }
    }

    void Recver(std::shared_ptr<Connection> connection)
    {
        int fd = connection->SockFd();
        while (1)
        {
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0); // 因为前面已经设置了套接字非阻塞，所以这里也是非阻塞读取
            if (n > 0)
            {
                // 添加到缓冲区中
                connection->AppendInBuffer(buffer);
            }
            else if (n == 0)
            {
                std::cout << "client close,peerip:" << connection->_ip << ",peerport:" << connection->_port
                          << ",sockfd:" << connection->SockFd() << std::endl;
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    std::cout << "client close,peerip:" << connection->_ip << ",peerport:" << connection->_port
                              << ",sockfd:" << connection->SockFd() << std::endl;
                    connection->_except_cb(connection);
                    return;
                }
            }
        }

        // 跳出循环表示有数据了，但不一定全 1.检测 2.有完整报文就处理
        _OnMessage(connection);
    }

    void Sender(std::shared_ptr<Connection> connection)
    {
        auto &outbuffer = connection->OutBuffer();
        while (1)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if (n > 0)
            {
                // 正常发送，那就清空发送缓冲区
                outbuffer.erase(0, n);
                if (outbuffer.empty())
                    break;
            }
            else if (n == 0)
            {
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    std::cout << "send error ...clientip:" << connection->_ip << ",clientport:" << connection->_port
                              << ",sockfd:" << connection->SockFd() << std::endl;
                    connection->_except_cb(connection);
                    return;
                }
            }
        }

        // 一般情况下，对写事件不关心，一次没发送完，那就需要设置写关心
        if (!outbuffer.empty())
        {
            EnableEvent(connection->SockFd(), true, true);
        }
        else
        {
            EnableEvent(connection->SockFd(), true, false);
        }
    }

    void Excepter(std::shared_ptr<Connection> connection)
    {
        int fd = connection->SockFd();
        std::cout << "Except Handle ...clientip:" << connection->_ip << ",clientport:" << connection->_port
                  << ",sockfd:" << connection->SockFd() << std::endl;
        // 出问题，那就要关闭套接字
        // 1.移除对fd的特定关心
        // EnableEvent(fd,false,false);
        _epoller_ptr->EpollUpdate(EPOLL_CTL_DEL, fd, 0);

        std::cout << "close done,sockfd:" << fd << std::endl;
        // 2.关闭套接字
        close(fd);

        std::cout << "remove done,sockfd:" << fd << std::endl;
        // 3.从unordered_map中移除
        _connections.erase(fd);
    }

    void EnableEvent(int sock, bool readable, bool writeable)
    {
        // 对读写事件不关心
        uint32_t events = 0;
        events |= (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
        // 设置进epoll模型中
        _epoller_ptr->EpollUpdate(EPOLL_CTL_MOD, sock, events);
    }

    // 检查文件描述符是否在map中保存
    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

    void Dispatcher(int timeout)
    {
        // 先看就绪队列有没有
        int n = _epoller_ptr->EpollWait(_revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t events = _revs[i].events;
            int sock = _revs[i].data.fd;
            // if(sock == _listensock_ptr->Fd()){}
            // 先判断event事件是读、写、异常事件；异常事件可以转换成读写事件
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);

            // 读写事件
            if ((events & EPOLLIN) && IsConnectionSafe(sock))
            {
                // 如果读回调函数不为空则调用
                if (_connections[sock]->_recv_cb)
                    _connections[sock]->_recv_cb(_connections[sock]);
            }
            if ((events & EPOLLOUT) && IsConnectionSafe(sock))
            {
                // 如果写回调函数不为空则调用
                if (_connections[sock]->_send_cb)
                    _connections[sock]->_send_cb(_connections[sock]);
            }
        }
    }
    void Loop()
    {
        _quit = false;
        while (!_quit)
        {
            // 分发事件
            Dispatcher(3000);
            //PrintConnection();
        }
        _quit = true;
    }

    void PrintConnection()
    {
        std::cout << "connection fd list:";
        for (auto &connection : _connections)
        {
            std::cout << connection.second->SockFd() << " ";
            std::cout << "inbuffer:" << connection.second->InBuffer();
        }
        std::cout << std::endl;
    }

    ~TcpServer()
    {
    }

private:
    std::shared_ptr<Sock> _listensock_ptr; // 监听socket
    std::shared_ptr<Epoller> _epoller_ptr;
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; // 文件描述符与连接的对应关系
    struct epoll_event _revs[num];                                     // 需要关注的事件,输出型参数
    uint16_t _port;
    bool _quit;

    func_t _OnMessage;
};
